package com.geeguo.ebuilder.operation.business.system.user.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.geeguo.ebuilder.core.common.exception.ApplicationException;
import com.geeguo.ebuilder.core.common.model.vo.PageListVO;
import com.geeguo.ebuilder.core.common.utils.SqlHelper;
import com.geeguo.ebuilder.core.redis.service.RedisCacheService;
import com.geeguo.ebuilder.core.common.utils.GsonUtils;
import com.geeguo.ebuilder.core.common.utils.ObjectEqualsHelper;
import com.geeguo.ebuilder.core.common.utils.PageUtils;
import com.geeguo.ebuilder.core.common.utils.key.UUIDGenerator;
import com.geeguo.ebuilder.operation.base.core.dictionary.CertificateTypeEnum;
import com.geeguo.ebuilder.core.redis.constants.CacheTimeConstants;
import com.geeguo.ebuilder.operation.base.redis.utils.CacheKeyFactory;
import com.geeguo.ebuilder.operation.business.system.logincertificate.model.LoginCertificateEntity;
import com.geeguo.ebuilder.operation.business.system.logincertificate.service.LoginCertificateService;
import com.geeguo.ebuilder.operation.business.system.organization.model.OrganizationCO;
import com.geeguo.ebuilder.operation.business.system.organization.service.OrganizationService;
import com.geeguo.ebuilder.operation.business.system.user.mapper.UserMapper;
import com.geeguo.ebuilder.operation.business.system.user.model.*;
import com.geeguo.ebuilder.operation.business.system.user.service.UserService;
import com.geeguo.ebuilder.operation.business.system.userrole.service.UserRoleService;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;

@Service
public class UserServiceImpl implements UserService {

    @Autowired
    private UserMapper userMapper;
    @Autowired
    private UserRoleService userRoleService;
    @Autowired
    private RedisCacheService redisCacheService;
    @Autowired
    private OrganizationService organizationService;
    @Autowired
    private LoginCertificateService loginCertificateService;

    @Override
    public PageListVO<UserVO> list(UserQuery query) {
        Map<String, Object> params = new HashMap<>();
        if (StringUtils.isNotEmpty(query.getUsername())) {
            params.put("username", SqlHelper.getFullImplict(query.getUsername()));
        }
        if (StringUtils.isNotEmpty(query.getRealName())) {
            params.put("realName", SqlHelper.getFullImplict(query.getRealName()));
        }
        if (StringUtils.isNotEmpty(query.getOrgId())) {
            List<String> childrenIds = organizationService.listChildrenIds(query.getOrgId());
            params.put("orgIds", childrenIds);
        }
        if (query.getState() != null && query.getState() > 0) {
            params.put("state", query.getState());
        }
        IPage<UserEntity> page = userMapper.list(new Page<>(query.getPageNo(), query.getPageSize()), params);
        List<UserVO> userList = new ArrayList<>();
        if (page.getRecords() != null && !page.getRecords().isEmpty()) {
            for (UserEntity item : page.getRecords()) {
                UserVO vo = UserConverter.INSTANCE.convert2VO(item);
                if (StringUtils.isNotEmpty(item.getOrgId())) {
                    OrganizationCO organization = organizationService.getCached(vo.getOrgId());
                    if (organization != null) {
                        vo.setOrgName(organization.getName());
                    }
                }
                userList.add(vo);
            }
        }
        return PageUtils.getPageListVO(page, userList);
    }

    @Override
    public PageListVO<UserSimpleVO> listSimple(UserQuery query) {
        Map<String, Object> params = new HashMap<>();
        if (StringUtils.isNotEmpty(query.getUsername())) {
            params.put("username", SqlHelper.getFullImplict(query.getUsername()));
        }
        if (StringUtils.isNotEmpty(query.getRealName())) {
            params.put("realName", SqlHelper.getFullImplict(query.getRealName()));
        }
        IPage<UserEntity> page = userMapper.listSimple(new Page<>(query.getPageNo(), query.getPageSize()), params);
        List<UserSimpleVO> userList = new ArrayList<>();
        if (page.getRecords() != null && !page.getRecords().isEmpty()) {
            for (UserEntity item : page.getRecords()) {
                UserSimpleVO vo = UserConverter.INSTANCE.convert2SimpleVO(item);
                if (StringUtils.isNotEmpty(item.getOrgId())) {
                    OrganizationCO organization = organizationService.getCached(vo.getOrgId());
                    if (organization != null) {
                        vo.setOrgName(organization.getName());
                    }
                }
                userList.add(vo);
            }
        }
        return PageUtils.getPageListVO(page, userList);
    }

    @Override
    public List<UserSimpleVO> search(String realName) {
        List<UserSimpleVO> result = null;
        List<UserEntity> list = userMapper.search(SqlHelper.getFullImplict(realName));
        if (list != null && !list.isEmpty()) {
            result = new ArrayList<>();
            for (UserEntity item : list) {
                UserSimpleVO vo = UserConverter.INSTANCE.convert2SimpleVO(item);
                if (StringUtils.isNotEmpty(item.getOrgId())) {
                    OrganizationCO organization = organizationService.getCached(vo.getOrgId());
                    if (organization != null) {
                        vo.setOrgName(organization.getName());
                    }
                }
                result.add(vo);
            }
        }
        return result;
    }

    @Override
    public UserEntity get(String id) {
        return userMapper.get(id);
    }

    @Override
    public UserCO getCached(String id) {
        UserCO result = null;
        String cacheKey = CacheKeyFactory.INSTANCE.getUser(id);
        String cacheJson = redisCacheService.get(cacheKey);
        if (StringUtils.isNotEmpty(cacheJson)) {
            result = GsonUtils.fromJson(cacheJson, UserCO.class);
        }
        if (result == null) {
            UserEntity entity = this.get(id);
            if (entity != null) {
                result = UserConverter.INSTANCE.convert2CO(entity);
                cacheJson = GsonUtils.toJson(result);
                redisCacheService.set(cacheKey, cacheJson, CacheTimeConstants.CACHE_WEEK);
            }
        }
        return result;
    }

    @Override
    public UserEntity getByCertificate(String certificate) {
        UserEntity user = null;
        LoginCertificateEntity loginCertificate = loginCertificateService.getByCertificate(certificate);
        if (loginCertificate != null) {
            user = userMapper.get(loginCertificate.getUserId());
        }
        return user;
    }

    @Override
    @Transactional
    public boolean save(UserEntity user, List<String> roles) {
        if (StringUtils.isBlank(user.getUsername())) {
            user.setUsername(null);
        }
        if (StringUtils.isBlank(user.getMobile())) {
            user.setMobile(null);
        }
        if (StringUtils.isBlank(user.getEmail())) {
            user.setEmail(null);
        }
        boolean result = userMapper.insert(user) > 0;
        if (result && StringUtils.isNotEmpty(user.getUsername())) {
            LoginCertificateEntity loginCertificate = new LoginCertificateEntity();
            loginCertificate.setId(UUIDGenerator.getId());
            loginCertificate.setUserId(user.getId());
            loginCertificate.setCertificate(user.getUsername());
            loginCertificate.setCertificateType(CertificateTypeEnum.Username.getValue());
            loginCertificate.setCreateTime(user.getCreateTime());
            loginCertificate.setCreateUser(user.getCreateUser());
            result = loginCertificateService.save(loginCertificate);
        }
        if (result && StringUtils.isNotEmpty(user.getMobile())) {
            LoginCertificateEntity loginCertificate = new LoginCertificateEntity();
            loginCertificate.setId(UUIDGenerator.getId());
            loginCertificate.setUserId(user.getId());
            loginCertificate.setCertificate(user.getMobile());
            loginCertificate.setCertificateType(CertificateTypeEnum.Mobile.getValue());
            loginCertificate.setCreateTime(user.getCreateTime());
            loginCertificate.setCreateUser(user.getCreateUser());
            result = loginCertificateService.save(loginCertificate);
        }
        if (result && StringUtils.isNotEmpty(user.getEmail())) {
            LoginCertificateEntity loginCertificate = new LoginCertificateEntity();
            loginCertificate.setId(UUIDGenerator.getId());
            loginCertificate.setUserId(user.getId());
            loginCertificate.setCertificate(user.getEmail());
            loginCertificate.setCertificateType(CertificateTypeEnum.Email.getValue());
            loginCertificate.setCreateTime(user.getCreateTime());
            loginCertificate.setCreateUser(user.getCreateUser());
            result = loginCertificateService.save(loginCertificate);
        }
        if (result && CollectionUtils.isNotEmpty(roles)) {
            result = userRoleService.batchSaveUserRole(roles, user.getId(), user.getCreateUser());
        }
        if (!result) {
            throw new ApplicationException("保存用户失败。");
        }
        return result;
    }

    @Override
    public boolean update(UserEntity current, UserEntity old, List<String> roles) {
        boolean result = userRoleService.batchSaveUserRole(roles, current.getId(), current.getModifyUser());
        Map<String, Object> changeValues = ObjectEqualsHelper.equals(current, old, new String[] {"username", "realName",
                "gender", "mobile", "email", "orgId", "postId", "hireDate", "state"});
        if (changeValues.isEmpty()) {
            return result;
        } else {
            changeValues.put("id", current.getId());
            changeValues.put("modifyTime", current.getModifyTime());
            changeValues.put("modifyUser", current.getModifyUser());
            result = userMapper.updateByParams(changeValues) == 1;
            if (result) {
                removeCached(current.getId());
            }
        }
        if (!result) {
            throw new ApplicationException("更新用户失败。");
        }
        return true;
    }

    @Override
    public boolean updatePassword(String id, String password, Date modifyTime, String modifyUser) {
        return userMapper.updatePassword(id, password, modifyTime, modifyUser) == 1;
    }

    @Override
    public boolean updateLastLogin(String id) {
        LambdaUpdateWrapper<UserEntity> update = new LambdaUpdateWrapper<>();
        update.set(UserEntity::getLastLoginTime, new Date());
        update.eq(UserEntity::getId, id);
        return userMapper.update(update) == 1;
    }

    @Override
    @Transactional
    public boolean remove(String id) {
        boolean result = userMapper.deleteById(id) == 1;
        if (result) {
            result = loginCertificateService.removeByUserId(id);
        }
        if (result) {
            result = userRoleService.removeByUserId(id);
        }
        if (result) {
            removeCached(id);
        }
        if (!result) {
            throw new ApplicationException("删除用户失败。");
        }
        return result;
    }

    @Override
    public boolean existsUsername(String username) {
        LambdaQueryWrapper<UserEntity> query = new LambdaQueryWrapper<>();
        query.eq(UserEntity::getUsername, username);
        return userMapper.selectCount(query) > 0;
    }

    @Override
    public boolean existsMobile(String mobile) {
        LambdaQueryWrapper<UserEntity> query = new LambdaQueryWrapper<>();
        query.eq(UserEntity::getMobile, mobile);
        return userMapper.selectCount(query) > 0;
    }

    @Override
    public boolean existsEmail(String email) {
        LambdaQueryWrapper<UserEntity> query = new LambdaQueryWrapper<>();
        query.eq(UserEntity::getEmail, email);
        return userMapper.selectCount(query) > 0;
    }

    private void removeCached(String id) {
        String cacheKey = CacheKeyFactory.INSTANCE.getUser(id);
        redisCacheService.del(cacheKey);
    }
}
